package org.canaan.quartz;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.*;
import org.springframework.scheduling.quartz.JobMethodInvocationFailedException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.util.MethodInvoker;

import java.lang.reflect.InvocationTargetException;
import java.util.List;

/**
 @Author Canaan
 @Date 2018/3/16 */
public interface JobDetailBuilder {

    JobDetail build(String key);

    void setTargetObject(Object targetObject);

    void setTargetMethod(String targetMethod);

    void setTargetClass(Class<?> targetClass);

    void setStaticMethod(String staticMethod);

    void setArguments(Object[] arguments);

    JobDetailBuilder setName(String name);

    JobDetailBuilder setGroup(String group);

    JobDetailBuilder setConcurrent(boolean concurrent);

    JobDetailBuilder setTargetBeanName(String targetBeanName);

    @PersistJobDataAfterExecution
    @DisallowConcurrentExecution
    class StatefulMethodInvokingJob extends JobDetailBuilder.MethodInvokingJob {
        public StatefulMethodInvokingJob() {
        }
    }

    class MethodInvokingJob extends QuartzJobBean {
        protected static final Log logger = LogFactory.getLog(JobDetailBuilder.MethodInvokingJob.class);
        private MethodInvoker                  methodInvoker;
        private List<JobDetailExecuteObserver> observers;

        public MethodInvokingJob() {

        }

        public void setMethodInvoker(MethodInvoker methodInvoker) {
            this.methodInvoker = methodInvoker;
        }

        public void setObservers(List<JobDetailExecuteObserver> observers) {
            this.observers = observers;
        }

        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            beforeNotice(context);
            try {
                if (!methodInvoker.isPrepared()) {
                    methodInvoker.prepare();
                }
                context.setResult(this.methodInvoker.invoke());

                afterNotice(context);
            } catch (InvocationTargetException var3) {
                exceptionNotice(context, var3);
                if (var3.getTargetException() instanceof JobExecutionException) {
                    throw (JobExecutionException) var3.getTargetException();
                } else {
                    throw new JobMethodInvocationFailedException(this.methodInvoker, var3.getTargetException());
                }
            } catch (Exception var4) {
                exceptionNotice(context, var4);
                throw new JobMethodInvocationFailedException(this.methodInvoker, var4);
            }

        }

        /**
         通知观察者
         @param context
         */
        private void exceptionNotice(JobExecutionContext context, Exception ex) {

            if (CollectionUtils.isEmpty(observers)) {
                return;
            }

            for (JobDetailExecuteObserver observer : observers) {
                try {
                    observer.exception(context, ex);
                } catch (Exception e) {
                    //
                }
            }
        }

        /**
         通知观察者
         @param context
         */
        private void afterNotice(JobExecutionContext context) {

            if (CollectionUtils.isEmpty(observers)) {
                return;
            }

            for (JobDetailExecuteObserver observer : observers) {
                try {
                    observer.after(context);
                } catch (Exception e) {
                    //
                }
            }
        }

        /**
         通知观察者
         @param context
         */
        private void beforeNotice(JobExecutionContext context) {

            if (CollectionUtils.isEmpty(observers)) {
                return;
            }

            for (JobDetailExecuteObserver observer : observers) {
                try {
                    observer.before(context);
                } catch (Exception e) {
                    //
                }
            }
        }
    }

}
